package com.lhkj.ct.base.utils.file.word.xwpf;

import cn.hutool.core.collection.CollectionUtil;

import com.lhkj.ct.base.enums.ReturnStatus;
import com.lhkj.ct.base.exception.BusinessException;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
  * <p>
  *     描述：导出word文档
  * </p>
  */
public class ExportWord {

    // 表格工具类
    private XWPFTableHelper xwpfTableHelper = null;
    // word 工具类

    private final String placeholder = "$";

    private final String prefix = "{";

    private final String suffix = "}";

    private String regex = "\\" + placeholder + "\\" + prefix + "([^\\}]+)\\" + suffix;

    public ExportWord() {
        xwpfTableHelper = new XWPFTableHelper();
    }

    public XWPFTableHelper getXwpfTableHelper() {
        return xwpfTableHelper;
    }

    public void setXwpfTableHelper(XWPFTableHelper xwpfTableHelper) {
        this.xwpfTableHelper = xwpfTableHelper;
    }

    /**
     * 设置 word 文档页面大小和横竖版 (word中1厘米约等于567)
     *
     * @param document        文档对象
     * @param width           页面宽度
     * @param height          页面高度
     * @param pageOrientation 页面排版
     */
    public void setPageSize(XWPFDocument document, Long width, Long height, STPageOrientation.Enum pageOrientation) {
        CTDocument1 ctDocument1 = document.getDocument();
        CTBody body = ctDocument1.getBody();
        if (!body.isSetSectPr()) {
            body.addNewSectPr();
        }
        CTSectPr section = body.getSectPr();

        if (!section.isSetPgSz()) {
            section.addNewPgSz();
        }
        //1. 设置页面大小  当前A4大小
        CTPageSz pageSize = section.getPgSz();
        //1.1 设置排版 必须要设置下面两个参数，否则整个的代码是无效的
        pageSize.setH(BigInteger.valueOf(height));
        pageSize.setW(BigInteger.valueOf(width));
        pageSize.setOrient(pageOrientation);
    }

    /**
     * 为表格插入数据，行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     * @param align     表格单元格文本水平对齐方式
     * @param vAlign    表格单元格文本垂直对齐方式
     */
    public void insertTable(XWPFTable table, List<List<Object>> tableList, STJc.Enum align, STVerticalJc.Enum vAlign) {
        // 设置表格边框
        CTTblBorders borders = table.getCTTbl().getTblPr().addNewTblBorders();
        CTBorder hBorder = borders.addNewInsideH();
        hBorder.setVal(STBorder.Enum.forString("single")); // 线条类型
        hBorder.setSz(new BigInteger("1")); // 线条大小
        hBorder.setColor("000000"); // 设置颜色
        borders.setInsideV(hBorder);
        borders.setLeft(hBorder);
        borders.setRight(hBorder);
        borders.setTop(hBorder);
        borders.setBottom(hBorder);

        //创建行,根据需要插入的数据添加新行，不处理表头
        for (int i = 0; i < tableList.size(); i++) {
            table.createRow();
        }
        //遍历表格插入数据
        int length = table.getRows().size();
        for (int i = 1; i < length; i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                CTTc cttc = cell.getCTTc();
                CTTcPr ctPr = cttc.addNewTcPr();
                // 设置单元格内容垂直对齐方式
                ctPr.addNewVAlign().setVal(vAlign);
                // 设置单元格内容水平对齐方式
                cttc.getPList().get(0).addNewPPr().addNewJc().setVal(align);
                Object data = tableList.get(i - 1).get(j);
                cell.setText(null != data ? String.valueOf(data) : "");
            }
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    public void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            this.replaceInPara(para, params);
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    public void replaceInPara(XWPFParagraph para, Map<String, Object> params) {
        StringBuilder runText = new StringBuilder();
        if (CollectionUtil.isEmpty(para.getRuns())) {
            return;
        }
        for (XWPFRun run : para.getRuns()) {
            String text = run.getText(0);
            if (text == null) {
                continue;
            }
            runText.append(text);
            run.setText("", 0);
        }
        para.getRuns().get(0).setText(parseTemplate(runText.toString(), params));
    }

    /**
     * 替换文档里面所有表格里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    public void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        while (iterator.hasNext()) {
            replaceInTable(iterator.next(), params);
        }
    }


    /**
     * 替换指定的表格里面的变量
     *
     * @param table  要替换的表格
     * @param params 参数
     */
    public void replaceInTable(XWPFTable table, Map<String, Object> params) {
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        if (table.getRows().size() > 0) {
            //判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
            if (matcher(table.getText()).find()) {
                rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        paras = cell.getParagraphs();
                        for (XWPFParagraph para : paras) {
                            replaceInPara(para, params);
                        }
                    }
                }
            }
        }

    }

    /**
     * 模板解析替换
     */
    public static String parseTemplate(String template, Map properties) {
        if (StringUtils.isBlank(template) || properties == null) {
            return template;
        }
        Matcher matcher = matcher(template);
        while (matcher.find()) {
            String group = matcher.group();
            Object o = properties.get(group.replaceAll("\\$\\{([^\\}]+)\\}", "$1"));
            if (o != null) {
                template = template.replace(group, String.valueOf(o));
            } else {
                template = template.replace(group, "");
            }
        }
        return template;
    }


    /**
     * 正则匹配字符串
     *
     * @param text 匹配的文本
     * @return
     */
    private static Matcher matcher(String text) {
        Pattern pattern = Pattern.compile("\\$\\{([^\\}]+)\\}", Pattern.CASE_INSENSITIVE);
        return pattern.matcher(text);
    }

    /**
     * 插入复制模板表格
     */
    public void insetCopyTable(XWPFDocument doc, XWPFTable odlTable, Map<String, Object> params) {
        // 创建新的 CTTbl ， table
        CTTbl ctTbl = CTTbl.Factory.newInstance();
        // 复制原来的CTTbl
        ctTbl.set(odlTable.getCTTbl());
        IBody iBody = odlTable.getBody();
        try {
            BeanUtils.copyProperties(odlTable.getBody(), iBody);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        XWPFTable newTable = new XWPFTable(ctTbl, iBody);
        // 替换复制的模板表格占位符数据
        replaceInTable(newTable, params);
        // 创建一个空的Table
        doc.createTable();
        // 设置table值
        doc.setTable(doc.getTables().size() - 1, newTable); // 将table设置到word中
    }

    /**
     * 插入复制模板表格
     */
    public XWPFTable copyTable(XWPFTable target) {
        // 创建新的 CTTbl ， table
        CTTbl ctTbl = CTTbl.Factory.newInstance();
        // 复制原来的CTTbl
        ctTbl.set(target.getCTTbl());
        IBody iBody = target.getBody();
        try {
            BeanUtils.copyProperties(target.getBody(), iBody);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            throw new BusinessException(ReturnStatus.SC_INTERNAL_SERVER_ERROR, "表格复制异常");
        }
        return new XWPFTable(ctTbl, iBody);
    }
}
